#9.1创建和使用类

#方法__init__()是一个特殊的方法，每当根据Dog类创建新实例时，Python都会自动运行它。
# 在这个方法的名称中，开头和末尾各有两个下划线，这是一种约定，旨在避免Python默认方法与普通方法发生名称冲突。
#方法__init__()让我们能够根据类创建实例时，同时设置属性。
#self参数是一个指向实例本身的引用，让实例能够访问类中的属性和方法。
class Dog():
    """一次模拟小狗的简单尝试"""
    def __init__(self, name, age):
        """初始化属性name和age"""
        self.name = name
        self.age = age

    def sit(self):
        """模拟小狗被命令时蹲下"""
        print(self.name.title() + " is now sitting.")

    def roll_over(self):
        """模拟小狗被命令时打滚"""
        print(self.name.title() + " rolled over!")
    
#9.1.2根据类创建实例
#要根据Dog类创建实例，可依次指定类名和用括号括起的必要信息：
my_dog = Dog('willie', 6)
print("My dog's name is " + my_dog.name.title() + ".")
print("My dog is " + str(my_dog.age) + " years old.")
#通过句点表示法访问实例的属性：
my_dog.sit()
my_dog.roll_over()

#9.1.3创建多个实例
your_dog = Dog('lucy', 3)
print("Your dog's name is " + your_dog.name.title() + ".")
print("Your dog is " + str(your_dog.age) + " years old.")
your_dog.sit()
your_dog.roll_over()

#练习
#9-1
class Restaurant():
    """一次模拟餐馆的简单尝试"""
    def __init__(self, restaurant_name, cuisine_type):
        """初始化属性name和age"""
        self.restaurant_name = restaurant_name
        self.cuisine_type = cuisine_type

    def describe_restaurant(self):
        """打印餐馆的名称和类型"""
        print("The restaurant's name is " + self.restaurant_name.title() + ".")
        print("The restaurant's cuisine type is " + self.cuisine_type + ".")

    def open_restaurant(self):
        """打印餐馆正在营业"""
        print("The restaurant is opening now.")

my_restaurant = Restaurant('michael', 'chinese')
print("My restaurant's name is " + my_restaurant.restaurant_name.title() + ".")
print("My restaurant's cuisine type is " + my_restaurant.cuisine_type + ".")
my_restaurant.describe_restaurant()
my_restaurant.open_restaurant()

#9-2使用类和实例
class car():
    """一次模拟汽车的简单尝试"""
    def __init__(self, make, model, year):
        """初始化属性name和age"""
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0                #给属性指定默认值时，在方法__init__()的定义中可不用包含为它提供初始值的形参。
                                                 #在方法__init__()中，形参self必不可少，还必须在其他形参的前面。
    def get_descriptive_name(self):
        """打印汽车的名称和类型"""
        long_name = str(self.year) + ' ' + self.make + ' ' + self.model
        return long_name.title()

    def update_odometer(self, mileage):
        """将里程表读数设置为指定的值"""
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

    def read_odometer(self):
        """打印汽车的里程表读数"""
        print("This car has " + str(self.odometer_reading) + " miles on it.")

    def increment_odometer(self, miles):
        """将里程表读数增加指定的量"""
        if miles < 0:
            print("You can't roll back an odometer!")
        else:
            self.odometer_reading += miles

    def fill_gas_tank(self):
        """打印汽车的油箱容量"""
        print("This car has a 60L gas tank.")

my_new_car = car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())

#9-3修改属性的值
#直接修改属性的值：
my_new_car.odometer_reading = 23
print(my_new_car.odometer_reading)

my_new_car.update_odometer(3)
my_new_car.update_odometer(30)
my_new_car.read_odometer()

my_used_car = car('subaru', 'outback', 2013)
print(my_used_car.get_descriptive_name())
my_used_car.update_odometer(23500)
my_used_car.read_odometer()
my_used_car.increment_odometer(100)
my_used_car.read_odometer()

#9.3继承
#一个类继承另一个类时，它将自动获得另一个类的所有属性和方法；原有的类称为父类，而新类称为子类。
#子类继承了其父类的所有属性和方法，同时还可以定义自己的属性和方法。

#9.3.1子类的方法__init__() 
#创建子类的实例时，Python首先需要完成的任务是给父类的所有属性赋值。
#为此，子类的方法__init__()需要父类施以援手。
class ElectricCar(car):
    """电动汽车的独特之处"""
    def __init__(self, make, model, year):
        """初始化父类的属性"""
        super().__init__(make, model, year)   #super()是一个特殊函数，帮助Python将父类和子类关联起来。
        self.battery_size = 70                #子类定义了属性battery_size。
                                              #创建子类的实例时，Python将首先找到父类car，并将父类所有属性都复制到这个子类中，
                                              #然后，父类car中的所有方法都被复制到这个子类中。
    def describe_battery(self):             #子类定义了自己的方法describe_battery()。
        """打印一条描述电瓶容量的消息"""
        print("This car has a " + str(self.battery_size) + "-kWh battery.")

    def fill_gas_tank(self):                #子类定义了自己的方法fill_gas_tank()。
        """电动汽车没有油箱"""              #覆盖了父类的方法fill_gas_tank()。
                                            #取其精华，去其糟粕。
        print("This car doesn't need a gas tank!")

my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()

#9.3.2将实例用作属性
class Battery():
    """一次模拟电动汽车电瓶的简单尝试"""
    def __init__(self, battery_size=70):
        """初始化电瓶的属性"""
        self.battery_size = battery_size

    def describe_battery(self):
        """打印一条描述电瓶容量的消息"""
        print("This car has a " + str(self.battery_size) + "-kWh battery.")

    def get_range(self):
        """打印一条消息，指出电瓶的续航里程"""
        if self.battery_size == 70:
            range = 240
        elif self.battery_size == 85:
            range = 270

        message = "This car can go approximately " + str(range)
        message += " miles on a full charge."
        print(message)

class ElectricCar(car):
    """电动汽车的独特之处"""
    def __init__(self, make, model, year):
        """初始化父类的属性"""
        super().__init__(make, model, year)
        self.battery = Battery()               #将实例用作属性。

my_tesla = ElectricCar('tesla','model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()          #通过实例my_tesla来调用方法describe_battery()。
my_tesla.battery.get_range()                 #通过实例my_tesla来调用方法get_range()。

#导入类
#导入整个模块： import module_name
#导入特定的类： from module_name import class_name
#导入模块中的所有类： from module_name import *
#导入模块中的多个类： from module_name import class_name1, class_name2, class_name3